package com.jia.thumps.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.jia.constant.RedisConstant;
import com.jia.friends.constant.FollowRedisConstant;
import com.jia.friends.entity.Follow;
import com.jia.friends.service.FollowService;
import com.jia.result.ScrollResult;
import com.jia.sms.entity.User;
import com.jia.sms.holder.UserHolder;
import com.jia.sms.service.UserService;
import com.jia.thumps.constant.BlogRedisConstant;
import com.jia.thumps.entity.Blog;
import com.jia.thumps.mapper.BlogMapper;
import com.jia.thumps.service.BlogService;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.*;

/**
 * (Blog)表服务实现类
 *
 * @author makejava
 * @since 2024-10-09 00:11:07
 */
@Service("blogService")
public class BlogServiceImpl extends ServiceImpl<BlogMapper, Blog> implements BlogService {

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    public static final String LIKE_REDIS_PREFIX = RedisConstant.PROJECT_REDIS_PREFIX +
            BlogRedisConstant.THUMPS_UP_PREFIX + BlogRedisConstant.LIKE_PREFIX;

    public static final String FEED_REDIS_PREFIX = RedisConstant.PROJECT_REDIS_PREFIX +
            FollowRedisConstant.FRIEND_PREFIX + FollowRedisConstant.FEED_PREFIX;

    @Resource
    private UserService userService;

    @Resource
    private FollowService followService;

    @Override
    public Boolean like(Long id) {
        // 获取当前登录用户
        Long userId = UserHolder.getUser().getId();
        // 判断这个笔记有没有被点赞
        String likeKey = LIKE_REDIS_PREFIX + id;
//        Boolean member = stringRedisTemplate.opsForSet().isMember(likeKey, userId);
        Double score = stringRedisTemplate.opsForZSet().score(likeKey, String.valueOf(userId));
        // 如果没有 那就点赞
        // 数据库进行修改
        boolean isSuccess;
//        if (Boolean.FALSE.equals(member)) {
        if (Objects.isNull(score)) {
            isSuccess = update().setSql("liked = liked + 1").eq("id", id).update();
            // 缓存里 加入 当前这个用户
            if (isSuccess) {
                stringRedisTemplate.opsForZSet().add(likeKey, String.valueOf(userId), System.currentTimeMillis());
//                stringRedisTemplate.opsForSet().add(likeKey, String.valueOf(userId));
            }
        } else {
            // 如果已经有点赞 那就取消点赞
            // 修改数据库
            // 缓存里删除这个用户
            isSuccess = update().setSql("liked = liked - 1").eq("id", id).update();
            // 缓存里 加入 当前这个用户
            if (isSuccess) {
                stringRedisTemplate.opsForZSet().remove(likeKey, String.valueOf(userId));
            }
        }
        return true;
    }

    @Override
    public Page<Blog> queryByPage(Blog blog, Page page) {
        QueryWrapper<Blog> wrapper = new QueryWrapper<>();
        wrapper.eq(!Objects.isNull(blog.getShopId()), "shop_id", blog.getShopId());
        wrapper.likeRight(StringUtils.isNotEmpty(blog.getTitle()), "title", blog.getTitle());
        Page<Blog> blogPage = this.baseMapper.selectPage(page, wrapper);
        List<Blog> records = blogPage.getRecords();
        isBlogLiked(records);
        return blogPage;
    }

    private void isBlogLiked(List<Blog> records) {
        // 获取当前登录用户
        Long userId = UserHolder.getUser().getId();
        for (Blog blog : records) {
            String likeKey = LIKE_REDIS_PREFIX + blog.getId();
//            boolean member = Boolean.TRUE.equals(stringRedisTemplate.opsForSet().isMember(likeKey, userId));
            Double score = stringRedisTemplate.opsForZSet().score(likeKey, String.valueOf(userId));
            blog.setLike(!Objects.isNull(score));
        }
    }

    @Override
    public Blog queryById(Long id) {
        Blog blog = this.baseMapper.selectById(id);
        isBlogLiked(Collections.singletonList(blog));
        return blog;
    }

    @Override
    public Boolean insert(Blog blog) {
        // 获取用户
        Long userId = UserHolder.getUser().getId();
        blog.setUserId(userId);
        // 保存笔记
        boolean isSuccess = save(blog);
        if (!isSuccess) {
            throw new RuntimeException("Could not save blog");
        }
        // 发送给粉丝 写模式
        List<Follow> follows = followService.query().eq("follow_user_id", userId).list();
        for (Follow follow : follows) {
            Long fanId = follow.getUserId();
            String feedKey = FEED_REDIS_PREFIX + fanId;
            stringRedisTemplate.opsForZSet().add(feedKey, blog.getId().toString(), System.currentTimeMillis());
        }
        return true;
    }

    @Override
    public List<User> likeUser(Long id, Long top) {
        String likeKey = LIKE_REDIS_PREFIX + id;
        // 查询结果是有顺序的
        Set<String> ids = stringRedisTemplate.opsForZSet().range(likeKey, 0, top);
        // 如果使用了 IN 查询 那查询的结果 就不会按照参数的顺序查询
        // 新语句 ORDER BY FIELD
        String join = StrUtil.join(",", ids);
        List<User> users = userService.query().in("id", ids).last(
                "ORDER BY FIELD(id," + join + ")"
        ).list();
        return users;
    }

    @Override
    public ScrollResult queryOwnerInbox(Long lastId, Integer offset) {
        // 获取用户
        ScrollResult scrollResult = new ScrollResult();
        Long userId = UserHolder.getUser().getId();
        String feedKey = FEED_REDIS_PREFIX + userId;

        Set<ZSetOperations.TypedTuple<String>> typedTuples = stringRedisTemplate.opsForZSet()
                .reverseRangeByScoreWithScores(feedKey, 0, lastId, offset, 3);

        if (CollectionUtil.isEmpty(typedTuples)) {
            return scrollResult;
        }
        // 解析 blogId minTime offset
        List<String> blogIds = new ArrayList<String>(typedTuples.size());
        long minTime = 0;
        int of = 1; // 和最小时间 相同的个数 offset
        for (ZSetOperations.TypedTuple<String> typedTuple : typedTuples) {
            // 获取id
            String id = typedTuple.getValue();
            blogIds.add(id);
            // 获取分数（时间戳）
            // 不停的覆盖 到最后一个 时间就是最小的了
            long time = typedTuple.getScore().longValue();
            if (time == minTime) {
                of++;
            } else {
                minTime = time;
                of = 1;
            }
        }
        String join = StrUtil.join(",", blogIds);
        List<Blog> blogs = query().in("id", blogIds)
                .last("ORDER BY FIELD(id," + join + ")").list();
        isBlogLiked(blogs);
        scrollResult.setList(blogs);
        scrollResult.setMinTime(minTime);
        scrollResult.setOffset(of);
        return scrollResult;
    }
}
